//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*
 * serial-pxa.c: Intel PXA serial port driver
 *
 * Copyright (c) 2003, Intel Corporation (yu.tang@intel.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include <elatypes.h>
#include <bulverde.h>

#define UART_RBR    BTRBR
#define UART_THR    BTTHR
#define UART_LCR    BTLCR
#define UART_LSR    BTLSR
#define UART_IER    BTIER
#define UART_FCR    BTFCR
#define UART_DLL    BTDLL
#define UART_DLH    BTDLH
#define UART_MCR    BTMCR

#define  XTAL   14745600

static int pxa_serial_change_speed(unsigned long baud)
{

//  int divisor = XTAL / (baud<<4);
    unsigned long divisor;
    const unsigned long  lRates[6] = { 115200, 57600, 38400, 19200, 9600, 1200 };
    const unsigned long  lDivisors[6] = { 8, 16, 24, 48, 96, 768};
    /*
     * Check for a valid data rate.
     */
    int i;
    for (i = 0; i < 6; i++) {
        if (lRates[i] == baud) {
            break;
        }
    }
    if (i == 6) {
        return -1;
    }
    divisor = lDivisors[i];

    UART_LCR |= LCR_DLAB;

    UART_DLL = (divisor & 0xFF);
    UART_DLH = (divisor >> 8);

    UART_LCR &= ~LCR_DLAB;

    return 0;
}

/* Flush serial input queue.
 * Returns 0 on success or negative error number otherwise
 */
static int pxa_serial_flush_input(void)
{
    volatile UINT32 tmp;

    /* keep on reading as long as the receiver is not empty */
    while (UART_LSR & LSR_DR) {
        if (UART_LSR & (LSR_PE | LSR_FE | LSR_BI))
            return -14;

        tmp = UART_RBR;
    }

    return 0;
}

/* Flush output queue.
 * Returns 0 on success or negative error number otherwise
 */
static int pxa_serial_flush_output(void)
{
    /* wait until the transmitter is ready to transmit*/
    while( !(UART_LSR & LSR_TDRQ) );

    return 0;
}

/* Initialise serial port at the request baudrate.
 * Returns 0 on success, or a negative error number otherwise.
 *
 *          14.7456 MHZ
 * BaudRate  =   ----------------
 *          16 x Divisor
 *
 * thus, if divisor is 24, the baud rate is 38400 bps.
 */
static int pxa_serial_init(unsigned long baud)
{
    CKEN |= CKEN6_FFUART;//BUGBUG: what about CKEN6_BTUART ?
    /* set the port to sensible defaults (no break, no interrupts,
     * no parity, 8 databits, 1 stopbit, transmitter and receiver
     * enabled)
     */
    UART_LCR = 0x3;

    /* assert DTR & RTS to avoid problems of hardware handshake
     * with serial terminals
     */
    UART_MCR = MCR_DTR | MCR_RTS ;

    /* enable FIFO */
    UART_FCR = FCR_TRFIFOE;

    /* enable UART  */
    UART_IER = IER_UUE;

    pxa_serial_change_speed(baud);

    pxa_serial_flush_output();

    return 0;
}

/* Check if there is a character available to read.
 * Returns 1 if there is a character available, 0 if not,
 * and negative error number on failure.
 */
static int pxa_serial_poll(void)
{
    /* check for errors */
    if (UART_LSR & (LSR_PE | LSR_FE | LSR_BI))
        return -14;

    if (UART_LSR & LSR_DR)
        return 1;
    else
        return 0;
}

/* read one character from the serial port. return character (between
 * 0 and 255) on success, or negative error number on failure. this
 * function is blocking */
static int pxa_serial_read(void)
{
    int rv;

    //for(;;)
    {
        rv = pxa_serial_poll();

        if (rv <= 0)
            return -1;
        else
            return  UART_RBR & 0xff;
    }
}

/* write character to serial port. return 0 on success, or negative
 * error number on failure. this function is blocking
 */
static int pxa_serial_write(int c)
{
    int count=0;
    /* wait for room in the transmit FIFO */
    while ((UART_LSR & LSR_TDRQ) == 0) {
        count ++;
        if(count > 1000) return 0;
    }

    UART_THR = c & 0xff;

    return 0;
}

/////////////////////////////////////////////////////////////////////
int __cdecl DBG_PortInit(unsigned long Port, unsigned long Baud)
{
    return pxa_serial_init(Baud);
}

void __cdecl DBG_SendChar(unsigned char c)
{
    pxa_serial_write(c);
}

int __cdecl DBG_RecvChar()
{
//    return pxa_serial_read();
    if (UART_LSR & LSR_DR) {
        return UART_RBR & 0xff;
    }
    else {
        /* no bit ready */
        return -1;
    }
}
